/**
 * 
 */
package jvs.peer.xmpp;

import java.io.OutputStream;
import java.util.List;

import jvs.peer.io.MessageConstant;
import jvs.peer.sec.User;
import jvs.peer.util.Log;
import jvs.sh.CommandInterpreter;
import jvs.sh.Console;
import jvs.sh.IShell;

import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;

/**
 * @author qiangli
 * 
 */
public class XmppShellWrapper implements PacketListener, PacketFilter {

	public class MyShell extends XmppShell {

		public MyShell(XMPPConnection conn) {
			super(conn);
		}

		@Override
		public void exit() {
			super.exit();
			//
			disconnect();
		}

		public List<User> who() {
			return listUser();
		}

	}

	public class MyConsole implements Console {

		public OutputStream data() {
			throw new RuntimeException(
					"data stream not supported by this shell");
		}

		public void err(String s) {
			reply("500 " + s);
		}

		public void log(String s) {

		}

		public void ok(String s) {
			reply("200 " + s);
		}

		public void out(String s) {
			reply(s);
		}

		public void input(String s) {

		}

		public void status(String code, String s) {
			reply(code + " " + s);
		}
	}

	private XmppConnectHandler handler;
	private String peer;
	private String thread;
	private XMPPConnection conn;
	private MyConsole console;
	private MyShell shell;
	private CommandInterpreter interp;
	private User user;
	private XmppWebAdapter adapter;

	public XmppShellWrapper(XmppConnectHandler handler, String peer,
			String thread, XMPPConnection conn, XmppWebAdapter adapter) {
		this.handler = handler;
		this.peer = peer;
		this.thread = thread;
		this.conn = conn;
		this.adapter = adapter;
		//
		initShell();
	}

	private void initShell() {
		//
		String id = StringUtils.parseBareAddress(peer);
		String name = StringUtils.parseName(peer);
		String res = StringUtils.parseResource(peer);
		user = new User(id, name, res);
		console = new MyConsole();
		shell = new MyShell(conn);
		interp = new CommandInterpreter(adapter);
		interp.setConsole(console);
		interp.setShell(shell);
		//
		shell.init();
	}

	public void processPacket(Packet packet) {
		Message req = (Message) packet;
		if (Log.DEBUG) {
			Log.log(this, "@@@ " + this.getClass().getName() + " process packet from: "
				+ req.getFrom() + " type: " + req.getType() + " tid: "
				+ req.getThread() + " body: " + req.getBody());
		}
		String res = req.getBody();
		interp.parse(res);
	}

	public boolean accept(Packet packet) {
		if (packet == null || packet.getFrom() == null
				|| !(packet instanceof Message)) {
			return false;
		}
		if (XmppUtil.isLoopback(packet)) {
			return false;
		}
		Message msg = (Message) packet;
		
		if (msg.getType().equals(Message.Type.chat)
				&& peer.equals(packet.getFrom()) && msg.getProperty(MessageConstant.SHELL) == null) {
			return true;
		}
		
//		//either chat and same peer - IM client
//		if (msg.getType().equals(Message.Type.chat)
//				&& peer.equals(packet.getFrom()) && msg.getProperty(MessageConstant.SHELL) == null) {
//			return true;
//		}
		
		//same thread - MI session
		String tid = msg.getThread();
		if (tid != null && tid.equals(thread)) {
			return true;
		}
		
		return false;
	}

	private void reply(String body) {
		Message msg = new Message();
		msg.setThread(thread);
		msg.setTo(peer + Jabber.SLASH_RESOURCE_HOTWEB);
		msg.setType(Message.Type.chat);
		msg.setProperty(MessageConstant.SHELL, MessageConstant.response);
		msg.setBody(body);
		conn.sendPacket(msg);
	}

	public List<User> listUser() {
		try {
			return handler.listUser();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public void connect() {
		try {
			shell.connect(user);
			conn.addPacketListener(this, this);
		} catch (Exception e) {
			e.printStackTrace();
			shell.exit();
		}
	}

	public IShell getShell() {
		return shell;
	}

	public void disconnect() {
		conn.removePacketListener(this);
		conn = null;
		handler.terminate(peer);
	}

	public User getUser() {
		return user;
	}
}
